package com.pets.web.service.impl;

import cn.hutool.core.map.MapUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.pets.common.utils.StringUtils;
import com.pets.common.utils.uuid.UUID;
import com.pets.framework.manager.AsyncManager;
import com.pets.web.constant.Constant;
import com.pets.web.dto.WxConfigDto;
import com.pets.web.entity.PetsUser;
import com.pets.web.mapper.PetsUserMapper;
import com.pets.web.service.IPetsUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pets.web.vo.ApiWxConfigRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * <p>
 * 微信用户信息表 服务实现类
 * </p>
 *
 * @author zhurenwang
 * @since 2024-05-26
 */
@Slf4j
@Service
public class PetsUserServiceImpl extends ServiceImpl<PetsUserMapper, PetsUser> implements IPetsUserService {

    @Value("${wx.appId}")
    private String appId;

    @Value("${wx.secret}")
    private String secret;

    @Value("${wx.accessTokenUrl}")
    private String accessTokenUrl;

    @Value("${wx.userInfoUrl}")
    private String userInfoUrl;

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public String getOpenId(String code) {
        String url = String.format(accessTokenUrl, appId, secret, code);
        String str = restTemplate.getForObject(url, String.class);
        log.info("获取openID 请求微信url: {}   返回: {}", url, str);
        if (Objects.isNull(str)) {
            throw new RuntimeException("请求openID出错");
        }
        JSONObject result = JSONObject.parseObject(str);
        String errMsg = result.getString("errmsg");
        if (StringUtils.isNotEmpty(errMsg)) {
            throw new RuntimeException("非法code 请重新获取授权: "+ code);
        }
        String accessToken = result.getString("access_token");
        String openid = result.getString("openid");
        String scope = result.getString("scope");
        //非静默登录需要获取用户信息
        if (StringUtils.equals(scope, Constant.SNSAPI_USERINFO)){
            AsyncManager.me().execute(new AsyncTask(accessToken, openid, userInfoUrl, restTemplate, baseMapper));
        }
        return openid;
    }

    @Override
    public PetsUser getByOpenId(String openId) {
        return lambdaQuery().eq(PetsUser::getOpenId, openId).one();
    }

    @Override
    public List<PetsUser> list(PetsUser petsUser) {
        return lambdaQuery()
                .like(StringUtils.isNotEmpty(petsUser.getOpenId()), PetsUser::getOpenId, petsUser.getOpenId())
                .like(StringUtils.isNotEmpty(petsUser.getNickname()), PetsUser::getNickname, petsUser.getNickname())
                .orderByDesc(PetsUser::getCreateTime).list();
    }

    @Override
    public WxConfigDto getWxConfig(ApiWxConfigRequest request) {
        String ticket = redisTemplate.opsForValue().get(Constant.JS_TICKET);
        long millis = System.currentTimeMillis();
        String uuid = UUID.randomUUID().toString(true);
        WxConfigDto dto = new WxConfigDto();
        dto.setAppId(appId);
        dto.setTimestamp(String.valueOf(millis));
        dto.setNonceStr(uuid);
        Map<String, String> map = new TreeMap<>();
        map.put("noncestr", uuid);
        map.put("jsapi_ticket", ticket);
        map.put("timestamp", String.valueOf(millis));
        map.put("url", request.getUrl());
        String sortJoin = MapUtil.sortJoin(map, "&", "=", true);
        String sign = SecureUtil.sha1(sortJoin);
        dto.setSignature(sign);
        dto.setJsApiList(new ArrayList<>());
        return dto;
    }

    public static class AsyncTask extends TimerTask {

        private RestTemplate restTemplate;
        private String accessToken;
        private String openid;
        private String userInfoUrl;
        private BaseMapper<PetsUser> baseMapper;

        public AsyncTask(String accessToken, String openid, String userInfoUrl, RestTemplate restTemplate, BaseMapper<PetsUser> baseMapper) {
            this.accessToken = accessToken;
            this.openid = openid;
            this.userInfoUrl = userInfoUrl;
            this.restTemplate = restTemplate;
            this.baseMapper = baseMapper;
        }

        @Override
        public void run() {
            try{
                String url = String.format(userInfoUrl, accessToken, openid);
                String str = restTemplate.getForObject(url, String.class);
                Objects.requireNonNull(str);
                log.info("获取用户信息 请求微信url: {}   返回: {}", url, str);
                JSONObject result = JSONObject.parseObject(str);
                String errCode = result.getString("errcode");
                if (StringUtils.isNotEmpty(errCode)) {
                    throw new RuntimeException("请求userinfo错误");
                }
                //获取用户信息
                String nickname = result.getString("nickname");
                nickname = new String(nickname.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                String headImgUrl = result.getString("headimgurl");
                QueryWrapper qw = new QueryWrapper<>();
                qw.eq("open_id", openid);
                PetsUser user = baseMapper.selectOne(qw);
                if (Objects.isNull(user)) {
                    user = new PetsUser();
                    user.setOpenId(openid);
                    user.setNickname(nickname);
                    user.setHeadImgUrl(headImgUrl);
                    baseMapper.insert(user);
                }else {
                    user.setNickname(nickname);
                    user.setHeadImgUrl(headImgUrl);
                    baseMapper.updateById(user);
                }
                log.info("异步保存用户信息: {}", user);
            }catch (Exception e) {
                log.error("异步获取用户信息出错", e);
            }
        }
    }

}
